官方 Demo:https://vueuse.org/core/useScroll/#usescroll
這個 API 原始碼有點長,會先盡量依照功能拆分出來看,不然一次看一整坨可能有點吃不消 XD
x、y position
:水平、垂直捲軸捲動的距離。
參考官方 Demo,跟 x
、y
有關的功能主要有兩個:
x
、y
被外層更新的時候,指定 element 內部要跟著滾動到該位置。x
、y
數值。先來看第一個功能,外層更新可以參考官方 Demo,X、Y Position 有 input 框可以做輸入,這邊就略過 Demo code,直接看 useScroll:
// src/compositions/useScroll.js
import { computed, ref } from 'vue'
import { toValue, unrefElement } from '@/helper'
import { useEventListener } from '@/compositions/useEventListener'
export function useScroll(element, options = {}) {
const {
behavior = 'auto',
eventListenerOptions = {
capture: false,
passive: true,
},
} = options
const internalX = ref(0)
const internalY = ref(0)
const x = computed({
get() {
return internalX.value
},
set(x) {
scrollTo(x, undefined)
},
})
const y = computed({
get() {
return internalY.value
},
set(y) {
scrollTo(undefined, y)
},
})
function scrollTo(_x, _y) {
if (!window)
return
const _element = toValue(element)
if (!_element)
return
(_element instanceof Document ? window.document.body : _element)?.scrollTo({
top: toValue(_y) ?? y.value,
left: toValue(_x) ?? x.value,
behavior: toValue(behavior),
})
const scrollContainer
= (_element)?.document?.documentElement // window
|| (_element)?.documentElement // document
|| (_element)
if (x != null)
internalX.value = scrollContainer.scrollLeft
if (y != null)
internalY.value = scrollContainer.scrollTop
}
return {
x,
y,
}
}
先看資料流,useScroll return x
、y
出去,這個 x
、y
是帶有 getter & setter 的 computed,也就是說當上層修改 x
、y
值的時候,x
、y
的 setter 會被觸發,接著就會執行 scrollTo
function。
scrollTo
function 執行的時候,會呼叫 window 的 scrollTo 方法,把捲軸位置滾動到我們指定的數值。滾動到指定位置後,接下來做的事情是,讓 x
、y
數值跟滾動後的捲軸位置做同步。
這邊額外提一個小東西,x != null
是用寬鬆方式比較,等同於 x !== null && x !== undefined
。
到這邊第一個功能就看完了,接下來看第二個,在指定 element 上滾動的時候,要能及時更新 x
、y
數值:
// src/compositions/useScroll.js
// ... 略
export function useScroll(element, options = {}) {
// ... 略
import { useEventListener } from '@/compositions/useEventListener'
function scrollTo(_x, _y) {
// ... 略
}
const setArrivedState = (target) => {
if (!window)
return
const el = (
(target)?.document?.documentElement
|| (target)?.documentElement
|| unrefElement(target)
)
const scrollLeft = el.scrollLeft
internalX.value = scrollLeft
let scrollTop = el.scrollTop
// patch for mobile compatible
if (target === window.document && !scrollTop)
scrollTop = window.document.body.scrollTop
internalY.value = scrollTop
}
const onScrollHandler = (e) => {
if (!window)
return
const eventTarget = (
(e.target).documentElement ?? e.target
)
setArrivedState(eventTarget)
}
useEventListener(
element,
'scroll',
onScrollHandler,
eventListenerOptions,
)
return {
x,
y,
}
}
可以看到在 scroll 事件觸發時,會執行 onScrollHandler
這個 function,接著執行 setArrivedState
function,這兩個 function 之後都還會做其他事情,這邊先聚焦在目前這個功能上。
主要邏輯在 setArrivedState
,目前做的事滿單純的,就是把 target 的 scrollLeft 數值設定給 x
、scrollTop 數值設定給 y
。
比較值得注意的是這段:
// patch for mobile compatible
if (target === window.document && !scrollTop)
scrollTop = window.document.body.scrollTop
這段上網查了一下,mobile 裝置似乎無法使用 document.documentElement.scrollTop 取值,要改用 window.document.body.scrollTop。
GitHub PR:https://github.com/RhinoLee/30days_vue/pull/19/files
今天講了 useScroll X position
, Y position
的部分,明天會從 useScroll 的 arrivedState
開始看,順利的話也許明天可以看完。不過 useScroll 比較複雜一點,如果出現 part3 的話不是我在擠牙膏,是真的有點吃力 XD